<?php

namespace Yjius\fileshandler\oss;

use OSS\OssClient;
use Yjius\fileshandler\FilesHandlerBuilderInterface;

class OssBuilder extends OssClient implements FilesHandlerBuilderInterface
{
    static private $instance;

    private $accessKeyId = '';
    private $accessKeySecret = '';
    private $endpoint = '';
    private $bucket = '';
    private $storePath = '';

    public function __construct($params = [])
    {
        $this->setAccessKeyId(isset($params['accessKeyId']) ? $params['accessKeyId'] : '');
        $this->setAccessKeySecret(isset($params['accessKeySecret']) ? $params['accessKeySecret'] : '');
        $this->setEndpoint(isset($params['endpoint']) ? $params['endpoint'] : '');
        $this->setBucket(isset($params['bucket']) ? $params['bucket'] : '');
        $this->setStorePath(isset($params['storePath']) ? $params['storePath'] : '');
        parent::__construct($this->accessKeyId, $this->accessKeySecret, $this->endpoint, $isCName = false, $securityToken = NULL, $requestProxy = NULL);
    }

    private function __clone()
    {

    }

    //单例模式
    static public function getInstance($params)
    {
        if ($params['driver'] != 'oss') {
            throw new \Exception("此类仅支持OSS类");
        }
        if (empty($params['accessKeyId'])) {
            throw new \Exception("请传入OSS-accessKeyId配置参数");
        }
        if (empty($params['accessKeySecret'])) {
            throw new \Exception("请传入OSS-accessKeySecret配置参数");
        }
        if (empty($params['endpoint'])) {
            throw new \Exception("请传入OSS-endpoint配置参数");
        }
        if (empty($params['bucket'])) {
            throw new \Exception("请传入OSS-bucket配置参数");
        }
        if (!(self::$instance instanceof self)) {
            self::$instance = new self($params);
        }
        return self::$instance;
    }

    /**
     * 范围下载
     */
    public function getRangeObject($object, $range = '0-4')
    {
        // 获取0~4字节（包括0和4），共5个字节的数据。如果指定的范围无效（比如开始或结束位置的指定值为负数，或指定值大于文件大小），则下载整个文件。
        $options = array(OssClient::OSS_RANGE => $range);
        $content = self::getObject($this->bucket, $object, $options);
        return $content;
    }

    /**
     * 临时访问链接示例
     */
    public function getSignUrl($object, $timeout = 3600)
    {
        // 填写不包含Bucket名称在内的Object完整路径。
        //$object = "teaching_dev/qingboinit/098f6bcd4621d373cade4e832627b4f6.mp4";
        // 设置签名URL的有效时长为3600秒。
        // 生成GetObject的签名URL。
        $signedUrl = self::signUrl($this->bucket, $object, $timeout);
        return $signedUrl;
    }

    /**
     * Purpose: PHP上传单个文件
     * @date 2021/11/24 15:45
     */
    public function uploadOneFile($fileObject, $localFilePath, $options = NULL)
    {
        $res = parent::uploadFile($this->bucket, $fileObject, $localFilePath, $options);
        return $res['info']['url'];
    }

    /**
     * 批量删除oss文件
     */
    public function deleteFiles($fileObjects = [])
    {
        $objects = [];
        if (!empty($fileObjects)) {
            foreach ($fileObjects as $object) {
                $objects[] = $object;
            }
            $res = parent::deleteObjects($this->bucket, $objects);
            return $res;
        }
        return false;
    }

    public function checkFile($fileInfo, $tmpDir = '', $saveDir = '')
    {
        throw new \Exception("OSS不支持此方法");
    }

    public function upload($fileInfo, $tmpDir = '', $saveDir = '')
    {
        throw new \Exception("OSS不支持此方法");
    }

    public function merge($fileInfo, $tmpDir = '', $saveDir = '')
    {
        throw new \Exception("OSS不支持此方法");
    }

    /**
     * 前端获取上传验证签名
     * getPolicy
     */
    public function getPolicy()
    {
        $callbackUrl = '';
        $callback_param = array('callbackUrl' => urlencode($callbackUrl),
            'callbackBody' => 'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}',
            'callbackBodyType' => "application/x-www-form-urlencoded");
        $callback_string = json_encode($callback_param);
        $base64_callback_body = base64_encode($callback_string);
        $now = time();
        $expire = 300;  //设置该policy超时时间是10s. 即这个policy过了这个有效时间，将不能访问。
        $end = $now + $expire;
        $expiration = $this->gmt_iso8601($end);

        //最大文件大小.用户可以自己设置
        $condition = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000);
        $conditions[] = $condition;

        // 表示用户上传的数据，必须是以$dir开始，不然上传会失败，这一步不是必须项，只是为了安全起见，防止用户通过policy上传到别人的目录。
        $start = array(0 => 'starts-with', 1 => '$key', 2 => $this->storePath);
        $conditions[] = $start;
        $arr = array('expiration' => $expiration, 'conditions' => $conditions);
        $policy = json_encode($arr);
        $base64_policy = base64_encode($policy);
        $string_to_sign = $base64_policy;
        $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->accessKeySecret, true));

        $response = array();
        $response['accessid'] = $this->accessKeyId;
        $response['host'] = $this->bucket . '.' . $this->endpoint;
        $response['policy'] = $base64_policy;
        $response['signature'] = $signature;
        $response['expire'] = $end;
        $response['callback'] = $base64_callback_body;
        $response['dir'] = $this->storePath;  // 这个参数是设置用户上传文件时指定的前缀。
        return $response;
    }

    private function gmt_iso8601($time)
    {
        $dtStr = date("c", $time);
        $mydatetime = new \DateTime($dtStr);
        $expiration = $mydatetime->format(\DateTime::ISO8601);
        $pos = strpos($expiration, '+');
        $expiration = substr($expiration, 0, $pos);
        return $expiration . "Z";
    }

    /**
     * @return string
     */
    public function getAccessKeyId()
    {
        return $this->accessKeyId;
    }

    /**
     * @param string $accessKeyId
     */
    public function setAccessKeyId($accessKeyId)
    {
        $this->accessKeyId = $accessKeyId;
    }

    /**
     * @return string
     */
    public function getAccessKeySecret()
    {
        return $this->accessKeySecret;
    }

    /**
     * @param string $accessKeySecret
     */
    public function setAccessKeySecret($accessKeySecret)
    {
        $this->accessKeySecret = $accessKeySecret;
    }

    /**
     * @return string
     */
    public function getEndpoint()
    {
        return $this->endpoint;
    }

    /**
     * @param string $endpoint
     */
    public function setEndpoint($endpoint)
    {
        $this->endpoint = $endpoint;
    }

    /**
     * @return string
     */
    public function getBucket()
    {
        return $this->bucket;
    }

    /**
     * @param string $bsddata
     */
    public function setBucket($bsddata)
    {
        $this->bucket = $bsddata;
    }

    /**
     * @return string
     */
    public function getStorePath()
    {
        return $this->storePath;
    }

    /**
     * @param string $storePath
     */
    public function setStorePath($storePath)
    {
        $this->storePath = $storePath;
    }

}